package com.lpb.spring.lpbspring.aop;

import com.google.common.collect.Lists;
import com.lpb.spring.lpbspring.aop.advice.Advice;
import com.lpb.spring.lpbspring.aop.advisor.Advisor;
import com.lpb.spring.lpbspring.aop.advisor.PointcutAdvisor;
import com.lpb.spring.lpbspring.aop.pointcut.Pointcut;
import com.lpb.spring.lpbspring.aop.proxy.AopProxyFactory;
import com.lpb.spring.lpbspring.ioc.BeanFactory;
import com.lpb.spring.lpbspring.ioc.BeanFactoryAware;
import com.lpb.spring.lpbspring.ioc.BeanPostProcessor;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.util.ClassUtils;
import org.springframework.util.ReflectionUtils;

import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * @ClassName AdvisorAutoProxyCreator
 * @Description aop具体实现
 * 切入bean的某个方法进行增强，需要在bean初始化完成后进行 所以aop操作是bean的后置增强
 *
 *
 * @date 2022/12/6 00:23
 * @Version 1.0
 * @authoe linpengbo
 */
public class AdvisorAutoProxyCreator implements BeanPostProcessor, BeanFactoryAware {

    private BeanFactory benFactory;

    //所有的advisor(所有的切面)
    private List<Advisor> advisorList;

    //是否已经加载了所有的切面类
    private volatile boolean loadAllAdvisorFlag = false;


    /**beanfactory中 获取bean实例的过程中，对bean初始化后 都要走这个后置处理，来检查类是否存在aop增强
     * 如果存在就处理
     * @param bean
     * @param beanName
     * @return
     * @throws Exception
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws Exception {
          //如果bean本来就是advisor或者advice 不处理
         if(bean instanceof Advisor || bean instanceof Advice){
              return bean;
         }

        //查找当前bean的增强器
        List<Advisor> matchAdvisorList = getMatchAdvisorList(bean, beanName);
        if(CollectionUtils.isEmpty(matchAdvisorList)){
            return bean;
        }

        return AopProxyFactory.getAopProxy(beanName,benFactory.getBean(beanName),matchAdvisorList,benFactory).getProxy();
    }


    private List<Advisor> getMatchAdvisorList(Object bean,String beanName)throws Exception{
        //第一次执行 加载所有的advisor
        if(!loadAllAdvisorFlag){
            synchronized (this){
                if (!loadAllAdvisorFlag) {
                    advisorList=this.benFactory.getBeansOfTypeList(Advisor.class);
                    loadAllAdvisorFlag=true;
                }
            }
        }

        if(CollectionUtils.isEmpty(this.advisorList)){
            return null;
        }
        Class<?> beanClass = bean.getClass();
        List<Method> allMethod= getAllMethodForClass(beanClass);

        List<Advisor> resultAdvisorList=Lists.newArrayList();

        //找到属于增强当前bean的advisor
        for (Advisor ad:this.advisorList) {
            if(ad instanceof PointcutAdvisor){
                PointcutAdvisor pad=(PointcutAdvisor)ad;
                if(isPointcutMatchBean(pad,beanClass,allMethod)){
                    resultAdvisorList.add(pad);
                }
            }
        }
        return resultAdvisorList;
    }

    /**判断当前切面PointcutAdvisor 是否是增强当前bean的
     * @param pa
     * @param beanClass
     * @param methods
     * @return
     */
    private boolean isPointcutMatchBean(PointcutAdvisor pa, Class<?> beanClass, List<Method> methods) throws Exception {
        Pointcut p = pa.getPointcut();

        // 首先判断类是否匹配
        if (!p.matchClass(beanClass)) {
            return false;
        }
        // 再判断是否有增强的方法
        for (Method method : methods) {
            if (p.matchMethod(beanClass,method)) {
                return true;
            }
        }
        return false;
    }


    /**获取类的所有方法 以及他实现的接口的方法
     * @param clazz
     * @return
     */
    private List<Method>  getAllMethodForClass(Class<?> clazz){
        List<Method> allMethodList= Lists.newArrayList();
        //类所有的接口
        Set<Class<?>> allInterfacesForClassAsSet = ClassUtils.getAllInterfacesForClassAsSet(clazz);
        allInterfacesForClassAsSet.add(clazz);
        for (Class cla:allInterfacesForClassAsSet) {
            Method[] methodArr = ReflectionUtils.getAllDeclaredMethods(cla);
            if(methodArr==null||methodArr.length==0){
                continue;
            }
            allMethodList.addAll(Arrays.asList(methodArr));
        }
        return allMethodList;
    }




    @Override
    public void setBeanFactory(BeanFactory bf) {
        this.benFactory = bf;
    }


}
